package fr.jade.resources.joram.wrapper;

import java.util.Properties;
import java.util.StringTokenizer;

import javax.jms.TopicConnectionFactory;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;

import org.objectweb.fractal.api.Component;
import org.objectweb.fractal.api.NoSuchInterfaceException;
import org.objectweb.fractal.api.control.IllegalLifeCycleException;
import org.objectweb.fractal.api.control.LifeCycleController;
import org.objectweb.fractal.util.Fractal;
import org.objectweb.joram.client.jms.Queue;
import org.objectweb.joram.client.jms.admin.AdminException;
import org.objectweb.joram.client.jms.admin.AdminModule;
import org.objectweb.joram.client.jms.tcp.TopicTcpConnectionFactory;

import fr.jade.base.JadeBaseComponent;
import fr.jade.fraclite.api.control.NoSuchAttributeException;
import fr.jade.resources.joram.api.JndiInterface;
import fr.jade.resources.joram.api.QueueInterface;
import fr.jade.util.FractalNavigator;
import fr.jade.util.FractalUtil;
import fr.jade.util.JadeException;


public class QueueWrapper extends JadeBaseComponent implements QueueInterface, LifeCycleController {

	private String listAtt[] = {"threshold", "writers", "readers","period","producThreshold","consumThreshold","autoEvalThreshold","waitAfterClusterReq","jndi"};

	/**
	 * Maximum number of messages
	 */
	//private String nbMaxMsg;

	/**
	 * Threshold for this queue
	 */
	private String threshold = "";

	/**
	 * Writers
	 */
	private String writers = "";

	/**
	 * Readers
	 */
	private String readers = "";

	/**
	 * period of time before activating an  automatic reevaluation of the queues load factor
	 */
	private String period = "100";

	/**
	 * number of messages above which the queue is considered as over-loaded
	 */
	private String producThreshold = "25";

	/**
	 * number of pending receive requests above which the queue requests messages from other queues  of the cluster
	 */
	private String consumThreshold = "2";

	/**
	 * Indicates if the queues has to calculate its load factor automatically
	 */
	private String autoEvalThreshold = "true";

	/**
	 * time during which a queue which requested sth to the cluster should not do it again
	 */
	private String waitAfterClusterReq = "100";

	private String jndi = "no";

	private final String CLASS_NAME = "org.objectweb.joram.mom.dest.ClusterQueue";

	private Queue myQueue;

	private boolean started;

	private Properties serverConfig;

	/**
	 * Access to Jndi Context
	 */
	private Context context;

	public Queue getQueue(){
		return myQueue;
	}

	private void initializeContext() {
		JndiInterface jndiItf = null;
		try {
			jndiItf = (JndiInterface)Fractal.getBindingController(JoramUtils.getServerComponent(self)).lookupFc("jndi");
		} catch (NoSuchInterfaceException e1) {
			e1.printStackTrace();
		} catch (AdminException e1) {
			e1.printStackTrace();
		}
		System.out.println("JNDI: Host " + jndiItf.getHost() + " Port " + jndiItf.getPort());
		Properties jndiprops = new Properties();
		jndiprops.setProperty("java.naming.factory.initial","fr.dyade.aaa.jndi2.client.NamingContextFactory");
		jndiprops.setProperty("java.naming.factory.host", jndiItf.getHost());
		jndiprops.setProperty("java.naming.factory.port", jndiItf.getPort());

		Thread ct = Thread.currentThread();
		ClassLoader cl = ct.getContextClassLoader();
		ct.setContextClassLoader(getClass().getClassLoader());
		try {
			context = new InitialContext(jndiprops);
		} catch (NamingException e) {
			e.printStackTrace();
		}
		ct.setContextClassLoader(cl);
	}

	private void initServerConfig() throws NoSuchInterfaceException, AdminException, NoSuchAttributeException, JadeException{
		serverConfig = new Properties();
		Component server = JoramUtils.getServerComponent(self);
		serverConfig.put("hostName", FractalUtil.getAttributeController(FractalNavigator.getNode(server)).getAttribute("hostName"));
		serverConfig.put("port",JoramUtils.PORT_NUMBER_START + Integer.parseInt(FractalUtil.getAttributeController(server).getAttribute("sid")));
		serverConfig.put("sid",FractalUtil.getAttributeController(server).getAttribute("sid"));
		serverConfig.put("userAdmin", FractalUtil.getAttributeController(server).getAttribute("userAdmin"));
		serverConfig.put("passAdmin", FractalUtil.getAttributeController(server).getAttribute("passAdmin"));
	}

	private void setAccessRights() throws Exception {
		String token;
		StringTokenizer st;

		// Set writers
		st = new StringTokenizer(writers);
		while(st.hasMoreTokens()){
			token = st.nextToken();
			if (token.equals("all"))
				myQueue.setFreeWriting();
			else
				myQueue.setWriter(JoramUtils.getUserInterfaceFromName(self, token).getUser());
		}

		// Set readers
		st = new StringTokenizer(readers);
		while(st.hasMoreTokens()){
			token = st.nextToken();
			if (token.equals("all"))
				myQueue.setFreeReading();
			else
				myQueue.setReader(JoramUtils.getUserInterfaceFromName(self, token).getUser());
		}		
	}

	public void setProperties() throws JadeException{
		if (jndi.equals("yes")){
			initializeContext();
			try {
				context.bind(Fractal.getNameController(self).getFcName(), myQueue);
				System.out.println("Queue " + Fractal.getNameController(self).getFcName() + " bound in Jndi context");
			} catch (Exception e) {
				e.printStackTrace();
				throw new JadeException("",e);
			}
		}
	}

	public String getAttribute(String name) throws NoSuchAttributeException {
		/*if (name.equals("nbMaxMsg"))
			return this.nbMaxMsg;
		else */if (name.equals("threshold"))
			return this.threshold;
		else if (name.equals("readers"))
			return this.readers;
		else if (name.equals("writers"))
			return this.writers;
		else if (name.equals("jndi"))
			return this.jndi;
		else if (name.equals("period"))
			return this.period;
		else if (name.equals("producThreshold"))
			return this.producThreshold;
		else if (name.equals("consumThreshold"))
			return this.consumThreshold;
		else if (name.equals("autoEvalThreshold"))
			return this.autoEvalThreshold;
		else if (name.equals("waitAfterClusterReq"))
			return this.waitAfterClusterReq;
		else
			return super.getAttribute(name);
	}

	public String[] listFcAtt() {
		return super.concat(listAtt, super.listFcAtt());
	}

	public void setAttribute(String name, String value) throws NoSuchAttributeException {
		if (name.equals("threshold")){
			if(!value.equals("")){
				this.threshold = value;
			}
		}
		else if (name.equals("readers"))
			this.readers = value;
		else if (name.equals("writers"))
			this.writers = value;
		else if (name.equals("jndi"))
			this.jndi = value;
		else if (name.equals("period"))
			this.period = value;
		else if (name.equals("producThreshold"))
			this.producThreshold = value;
		else if (name.equals("consumThreshold"))
			this.consumThreshold = value;
		else if (name.equals("autoEvalThreshold"))
			this.autoEvalThreshold = value;
		else if (name.equals("waitAfterClusterReq"))
			this.waitAfterClusterReq = value;
		else
			super.setAttribute(name, value);
	}

	public String getFcState() {
		return started ? STARTED : STOPPED;
	}

	public void startFc() throws IllegalLifeCycleException {
	  if(!started && JoramUtils.serverIsStarted(self)){
  		try {
  			initServerConfig();
  
  			Properties queueProp = new Properties();
  			queueProp.setProperty("period", period);
  			queueProp.setProperty("producThreshold", producThreshold);
  			queueProp.setProperty("consumThreshold", consumThreshold);
  			queueProp.setProperty("autoEvalThreshold", autoEvalThreshold);
  			queueProp.setProperty("waitAfterClusterReq", waitAfterClusterReq);
  
  			TopicConnectionFactory con = TopicTcpConnectionFactory.create(serverConfig.getProperty("hostName"), JoramUtils.PORT_NUMBER_START + Integer.parseInt(serverConfig.getProperty("sid")));
  			AdminModule.connect(con, serverConfig.getProperty("userAdmin"), serverConfig.getProperty("passAdmin"));
  			myQueue = Queue.create(Integer.parseInt(serverConfig.getProperty("sid")),Fractal.getNameController(self).getFcName(),CLASS_NAME,queueProp);
  			System.out.println("Queue properties" + queueProp.toString());
  			setAccessRights();
  			/*if (nbMaxMsg != null)
  				myQueue.setNbMaxMsg(Integer.parseInt(nbMaxMsg));*/
  			if (!threshold.equals(""))
  				myQueue.setThreshold(Integer.parseInt(threshold));
  			AdminModule.disconnect();
  			setProperties();
  			started = true;
  		} catch (Exception e) {
  			e.printStackTrace();
  			//throw new JadeException("Error while starting the service");
  		}
	  }
	}

	public void stopFc() throws IllegalLifeCycleException {
	  if(started){
  		try {
  		  initServerConfig();
  		  String hostName = serverConfig.getProperty("hostName");
  		  log("hostName: " + hostName);
  		  String port = serverConfig.getProperty("port");
  		  log("port: " + port);
  		  
  		  TopicConnectionFactory con = TopicTcpConnectionFactory.create(serverConfig.getProperty("hostName"), JoramUtils.PORT_NUMBER_START + Integer.parseInt(serverConfig.getProperty("sid")));
  			AdminModule.connect(con, serverConfig.getProperty("userAdmin"), serverConfig.getProperty("passAdmin"));
  			myQueue.delete();
  			AdminModule.disconnect();
  			
  			System.out.println("Queue " + FractalNavigator.getName(self) + " deleted (Joram level)");
  			// Unbind the queue from Jndi if necessary
  			if (jndi.equals("yes")){
  				context.unbind(FractalNavigator.getName(self));
  				System.out.println("Queue " + FractalNavigator.getName(self) + " unbound from Jndi context");
  			}
  			started = false;
  		} catch (Exception e) {
  			e.printStackTrace();
  			//throw new JadeException("Error while stopping the service");
  		}
  		myQueue = null;
	  }
	}

	public String getMyServerSID() {
		if(serverConfig == null){
			try {
				initServerConfig();
			} catch (NoSuchInterfaceException e) {
				e.printStackTrace();
			} catch (AdminException e) {
				e.printStackTrace();
			} catch (NoSuchAttributeException e) {
				e.printStackTrace();
			} catch (JadeException e) {
				e.printStackTrace();
			}
		}
		return serverConfig.getProperty("sid");
	}

}
